package com.erf.codec;

import com.erf.message.BaseMessage;
import com.erf.tmpdb.DBMap;
import com.google.gson.*;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.lang.reflect.Type;
import java.util.List;

/**
 * @author zhangzy
 * @date 2022/7/5
 */
@Slf4j
public class ServerClassCodec extends MessageToMessageCodec<String, BaseMessage> {
    private static Gson GSON = null;

    public ServerClassCodec() {
        GSON = new GsonBuilder()
                .registerTypeAdapter(Exception.class, new ExceptionTypeAdapter())
                .registerTypeAdapter(Class.class, new ClassTypeAdapter())
                .create();
    }

    /**
     * 编码器
     */
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, BaseMessage baseMessage, List<Object> list) throws Exception {
        log.info("[server_send]" + baseMessage);
        Class<? extends BaseMessage> aClass = baseMessage.getClass();
        Integer integer = DBMap.classMap.get(aClass);
        baseMessage.setCommand(integer);
        //直接转化生效生效
        list.add(GSON.toJson(baseMessage));
    }

    /**
     * 解码器
     */
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, String baseMessageStr, List<Object> list) throws Exception {
        //第一次只能解析出报文头
        BaseMessage baseMessage = GSON.fromJson(baseMessageStr, BaseMessage.class);
        int command = baseMessage.getCommand();
        Class<?> aClass = DBMap.commandMap.get(command);
        Object o = GSON.fromJson(baseMessageStr, aClass);
        list.add(o);
    }

    public static class ClassTypeAdapter implements JsonSerializer<Class<?>>, JsonDeserializer<Class<?>> {

        @Override
        public JsonElement serialize(Class<?> src, Type typeOfSrc, JsonSerializationContext context) {
            return new JsonPrimitive(src.getName());
        }

        @Override
        public Class<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            try {
                return Class.forName(json.getAsString());
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public static class ExceptionTypeAdapter implements JsonSerializer<Exception>, JsonDeserializer<Exception> {

        @Override
        public Exception deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            String asString = json.getAsString();
            byte[] decode = HexBin.decode(asString);
            ByteArrayInputStream bis = null;
            ObjectInputStream ois = null;
            try {
                bis = new ByteArrayInputStream(decode);
                ois = new ObjectInputStream(bis);
                Exception exception = (Exception) ois.readObject();
                ois.close();
                bis.close();
                return exception;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (ois != null) {
                        ois.close();
                    }
                    if (bis != null) {
                        bis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return new Exception();
        }

        @Override
        public JsonElement serialize(Exception e, Type type, JsonSerializationContext jsonSerializationContext) {
            ByteArrayOutputStream out = null;
            ObjectOutputStream oos = null;
            try {
                out = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(out);
                oos.writeObject(e);
                byte[] bytes = out.toByteArray();
                oos.close();
                out.close();
                String encode = HexBin.encode(bytes);
                return new JsonPrimitive(encode);
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                try {
                    if (oos != null) {
                        oos.close();
                    }
                    if (out != null) {
                        out.close();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            return null;
        }
    }
}
